#pragma once

/*
    完成日志器模块：
    1、抽象日志器基类
    2、派生出不同的子类（同步日志器类 & 异步日志器类）
*/

#include "util.hpp"
#include <iostream>
#include <unordered_map>
#include <memory>
#include <mutex>
#include <string>
#include <atomic>
#include "format.hpp"
#include "Level.hpp"
#include <vector>
#include "sink.hpp"
#include "stdarg.h"
#include <cassert>
#include "looper.hpp"

namespace mian_yang
{
    class Logger
    {
    public:
        /*日志器基类，基本定义了日志器的打印操作*/
        using ptr = std::shared_ptr<Logger>;

        /*日志器构造，日志器的构造要有日志器的名称，
                        日志的限制等级。
                        日志的格式化器
                        日志的落地器*/
        Logger(const std::string &logger_name,
               LogLevel::value lv,
               Formatter::ptr &formatter,
               std::vector<LogSink::ptr> &sinks)
            : _logger_name(logger_name),
              _limit_level(lv),
              _formatter(formatter),
              _sinks(sinks)
        {
        }

        const std::string &Getname()
        {
            return _logger_name;
        }

        /*构造日志消息，格式化日志消息，使用落地器进行落地*/
        void debug(const std::string &file, size_t line,
                   const std::string &fmt, ...)
        {
            /*通过输入的参数构造日志消息对象，然后进行日志的
            格式化。*/

            // 1、判断是否受到限制
            if (LogLevel::value::DEBUG < _limit_level)
            {
                /*受限*/
                return;
            }

            /*对fmt格式化字符串进行格式化组织，组织后得到的
             */
            va_list ap;        /*这个可以理解为以一个数组*/
            va_start(ap, fmt); /*这个函数是将fmt以后的元素
                                 都保存到ap里面。*/
            char *res;
            /*将格式化信息拷贝到res中*/
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret < 0)
            {
                std::cout << "vasprintf failed!\n"
                          << std::endl;
                return;
            }
            va_end(ap);
            serialize(file, line, res, LogLevel::value::DEBUG);
        }

        void info(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            if (LogLevel::value::INFO < _limit_level)
            {
                /*受限*/
                return;
            }

            va_list ap; /*这个可以理解为以一个数组*/
            va_start(ap, fmt);

            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret < 0)
            {
                std::cout << "vasprintf failed!\n"
                          << std::endl;
                return;
            }
            va_end(ap);
            serialize(file, line, res, LogLevel::value::INFO);
        }

        void warn(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            if (LogLevel::value::WARN < _limit_level)
            {
                /*受限*/
                return;
            }

            va_list ap; /*这个可以理解为以一个数组*/
            va_start(ap, fmt);

            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret < 0)
            {
                std::cout << "vasprintf failed!\n"
                          << std::endl;
                return;
            }
            va_end(ap);
            serialize(file, line, res, LogLevel::value::WARN);
        }

        void error(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            if (LogLevel::value::ERROR < _limit_level)
            {
                /*受限*/
                return;
            }

            va_list ap; /*这个可以理解为以一个数组*/
            va_start(ap, fmt);

            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret < 0)
            {
                std::cout << "vasprintf failed!\n"
                          << std::endl;
                return;
            }
            va_end(ap);
            serialize(file, line, res, LogLevel::value::ERROR);
        }

        void fatal(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            if (LogLevel::value::FATAL < _limit_level)
            {
                /*受限*/
                return;
            }

            va_list ap; /*这个可以理解为以一个数组*/
            va_start(ap, fmt);

            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret < 0)
            {
                std::cout << "vasprintf failed!\n"
                          << std::endl;
                return;
            }
            va_end(ap);
            serialize(file, line, res, LogLevel::value::FATAL);
        }

    protected:
        void serialize(const std::string file, size_t line, char *res, LogLevel::value level)
        {
            /*构造msg对象*/
            mian_yang::LogMsg msg(file, line, _logger_name, level, res);
            /*通过格式化工具对LogMsg进行格式化， 得到格式化后
            的日志字符串*/
            std::string str = _formatter->format(msg);

            /*进行日志落地*/
            log(str.c_str(), str.size());
            free(res);
        }

        virtual void log(const char *data, size_t len) = 0;

    protected:
        std::mutex _mutex;        /*日志落地器，保证线程安全问题。用到互斥锁*/
        std::string _logger_name; /*日志器名称*/
        /*日志等级见哈*/
        std::atomic<LogLevel::value> _limit_level;
        /*日志格式化器*/
        mian_yang::Formatter::ptr _formatter;
        /*保存落地器*/
        std::vector<mian_yang::LogSink::ptr> _sinks;
        /*  整个的逻辑就是， 日志器名称用来传给msg
        保存日志其名称，然后根据日志等级限制来看到底打印
        到哪一步的日志。
            然后将得到的msg传给_formatter, 得到格式化后的
        日志信息，然后使用落地器将格式化后的数据进行打印*/
    };

    enum class LoggerType
    {
        LOGGER_SYNC,
        LOGGER_ASYNC
    };

    /*同步日志器类， 继承了日志器基类， 拥有了基本的
    打印日志操作， 差的只是具体的打印方式。*/
    class SyncLogger : public Logger
    {
    public:
        SyncLogger(const std::string &logger_name,
                   LogLevel::value lv,
                   Formatter::ptr &formatter,
                   std::vector<LogSink::ptr> &sink)
            : Logger(logger_name, lv, formatter, sink)
        {
        }

    protected:
        void log(const char *data, size_t len)
        { /*加锁*/
            std::unique_lock<std::mutex> lock(_mutex);
            if (_sinks.empty())
                return;
            for (auto &sink : _sinks)
            {
                /*落地器， 枷锁之后， 就把落地器
                都拿出来， 每一个落地器打印一次数据*/
                sink->log(data, len);
            }
        }
    };

    class AsyncLogger : public Logger
    {
    public:
        AsyncLogger(const std::string &logger_name,
                    LogLevel::value lv,
                    Formatter::ptr &formatter,
                    std::vector<LogSink::ptr> &sink,
                    AsyncType _looper_type)
            : Logger(logger_name, lv, formatter, sink),
              _looper(std::make_shared<AsyncLooper>(std::bind(&AsyncLogger::realLog,
                                                              this,
                                                              std::placeholders::_1),
                                                    _looper_type))
        {
        }
        void log(const char *data, size_t len) // 将数据写入缓冲区
        {
            _looper->push(data, len);   /*这里其实就是主线程去写数据, 子线程去执行threadentry.
            其中,主线程用来向生产者里面写数据, 就是保证生产者还有空间,有空间就写,没空间就阻塞.并且如果
            打印完了,就唤醒消费者.
                然后消费者就是醒着的时候第一件事情就是先把生产者的数据拿过来. 把自己的空缓冲区给他. 并且让他醒来,防止他写的太多阻塞睡眠了.*/
        }

        // 将缓冲区的数据进行实际落地
        void realLog(Buffer &buf)
        {
            if (_sinks.empty())
                return;
            for (auto &sink : _sinks)
            {
                sink->log(buf.begin(), buf.readAbleSize());
            }
        }

    private:
        AsyncLooper::ptr _looper;
    };

    /*有了日志器还不够， 还要有一个创建日志器的工厂，
    这个工厂可以创建两种日志器， 同步日志器和异步日志器*/
    class LoggerBuilder
    {
    public:
        LoggerBuilder()
            : _logger_type(LoggerType::LOGGER_SYNC),
              _limit_level(LogLevel::value::DEBUG),
              _looper_type(AsyncType::ASYNC_SAFE)
        {
        }

        // 构建日志类型
        void buildLoggerType(LoggerType type)
        {
            _logger_type = type;
        }

        /*构建日志器名称*/
        void buildLoggerName(const std::string &name)
        {
            _logger_name = name;
        }

        void buildLoggerLevel(const mian_yang::LogLevel::value level)
        {
            _limit_level = level;
        }

        void buildFormatter(const std::string &pattern)
        {
            _formatter = std::make_shared<Formatter>(pattern);
        }
        /*不传参构造格式化器， 重载*/
        void buildFormatter()
        {
            _formatter = std::make_shared<Formatter>();
        }

        /*落地器*/
        template <class SinkType, typename... Args>
        void buildSink(Args &&...args)
        {
            LogSink::ptr psink = SinkFactory<SinkType>::create(std::forward<Args>(args)...);
            _sinks.push_back(psink);
        }

        /*这个是建造日志器， 建造日志器函数根据建造的
        日志器类别的不同， 会有不同的效果。
        有两种创建日志器的方式， 一种是创建全局日志器，
        一种是创建局部日志器。*/
        virtual Logger::ptr build() = 0;

    protected:
        AsyncType _looper_type;                    /*如果是异步，异步的类别*/
        LoggerType _logger_type;                   /*日志器的类别*/
        std::mutex _mutex;                         /*日志落地要保证安全问题， 用到互斥锁*/
        std::string _logger_name;                  /*日志器名称*/
        std::atomic<LogLevel::value> _limit_level; /*日志等级检查使用*/
        Formatter::ptr _formatter;                 /*格式化器*/
        std::vector<LogSink::ptr> _sinks;          /*落地器对象*/
    };

    class LocallLoggerBuilder : public LoggerBuilder
    {
    public:
        Logger::ptr build() override
        {
            /*检查日志器名称*/
            assert(!_logger_name.empty());
            /*检查格式化器*/
            if (_formatter.get() == nullptr)
            {
                _formatter = std::make_shared<Formatter>();
            }
            /*检查落地器*/
            if (_sinks.empty())
            {
                buildSink<StdoutSink>();
            }

            /*都没有问题之后， 就根据日志器的类别
            进行创建*/
            /*异步日志器*/
            if (_logger_type == LoggerType::LOGGER_ASYNC)
            {
                return std::make_shared<AsyncLogger>(_logger_name, _limit_level,
                                                     _formatter, _sinks, _looper_type);
            }
            else
            {
                return std::make_shared<SyncLogger>(_logger_name, _limit_level,
                                                    _formatter, _sinks);
            }
        }

    private:
        std::mutex _mutex;
        Logger::ptr _root_logger; /*默认日志器*/
        std::unordered_map<std::string, Logger::ptr> _loggers;
    };

    /*全局日志器，单例管理*/
    class LoggerManager
    {
    public:
        static LoggerManager& GetInstance()
        {
            static LoggerManager eton;
            return eton;
        }
        void addLogger(Logger::ptr &logger)
        {
            /*
                检验logger是否存在， 如果已经存在了
                就return， 不需要重复添加。
            */
           if (hasLogger(logger->Getname())) return;
           std::unique_lock<std::mutex> lock(_mutex);
            /*就是向hash里面添加键值对*/
           _loggers.insert(std::make_pair(logger->Getname(), logger));
        }

        bool hasLogger(const std::string &name)
        {
            /*加锁*/
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _loggers.find(name);
            if (it == _loggers.end())
            {
                return false;
            }
            return false;
        }

        Logger::ptr getLogger(const std::string &name)
        {
            /*获取日志器*/
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _loggers.find(name);
            if (it == _loggers.end())
            {
                return Logger::ptr();
            }
            return it->second;
        }

        Logger::ptr rootLogger()
        {
            return _root_logger;
        }

    private:
        LoggerManager()
        {
            std::unique_ptr<mian_yang::LoggerBuilder> builder(new mian_yang::LocallLoggerBuilder());
            builder->buildLoggerName("root");
            builder->buildLoggerType(LoggerType::LOGGER_ASYNC);
            _root_logger = builder->build();
            _loggers.insert({"root", _root_logger});            
        }

    private:
    /*全局日志器管理器， 就是有一个root作为默认日志器
    然后其他的普通日志器利用日志名称进行key，本体
    作为value保存下来。*/
        std::mutex _mutex; 
        Logger::ptr _root_logger;
        std::unordered_map<std::string, Logger::ptr> _loggers;
    };

    /*设计一个全局日志器， 就是在局部的基础上增加了一个功能， 将日志器添加到单例对象中*/
    class GlobalLoggerBuilder : public LoggerBuilder
    {
        Logger::ptr build() override
        {
            /*必须要有日志器名称*/
            if(_logger_name.empty()) return Logger::ptr();
            /*只能指针的get接口获取原生指针*/
            if(_formatter.get() == nullptr)
            {
                _formatter = std::make_shared<Formatter>();
            }
            if (_sinks.empty())
            {
                buildSink<StdoutSink>();
            }

            Logger::ptr logger;

            /*异步日志器*/
            if (_logger_type == LoggerType::LOGGER_ASYNC)
            {
                logger = std::make_shared<AsyncLogger>(_logger_name, _limit_level, _formatter, _sinks, _looper_type);
            } 
            else
            {
                logger = std::make_shared<SyncLogger>(_logger_name, _limit_level, _formatter, _sinks);
            }
            LoggerManager::GetInstance().addLogger(logger);
            return logger;
        }
    };

}
